package cn.jingzhuan.element.demo.view.util.audio

import android.app.PendingIntent
import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import cn.jingzhuan.element.demo.App
import cn.jingzhuan.element.element.BuildConfig
import cn.jingzhuan.element.element.R


/**
 * Created by KNOX.
 */
class AudioService : Service(), Playback.Callback {
  
//  @Inject lateinit var presenter : MediaPresenter
  
  private var playback : Playback? = null
  private var audioBinder : AudioBinder? = null
  private var audioFocusRequestBuilder : AudioFocusRequest.Builder? = null
  private var audioManager : AudioManager? = null
  private var wakeLock : PowerManager.WakeLock? = null
  private var mediaSession : MediaSessionCompat? = null
  private var notification : AudioNotification? = null
  private var prepareStatus : PrepareStatus? = null
  private val audioFocusListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
    when (focusChange) {
      AudioManager.AUDIOFOCUS_GAIN -> {
        if (! isPlaying() && App.instance.lifecycle.isRunningInForeground())
          play()
      }
      AudioManager.AUDIOFOCUS_LOSS -> pause()
      AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
        if (isPlaying())
          pause()
      }
      AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> pause()
    }
  }

  companion object {
    private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID
    const val ACTION_PLAY_PAUSE = PACKAGE_NAME + ".play.pause"
    private const val MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE
  }

  override fun onBind(p0 : Intent?) : IBinder? {
    audioBinder = AudioBinder()
    return audioBinder
  }

  override fun onCreate() {
    super.onCreate()
    
//    DaggerMediaComponent.builder()
//        .mediaModule(MediaModule(this))
//        .build()
//        .inject(this)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      val audioAttribute = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .build()
      } else {
        null
      }
      audioFocusRequestBuilder = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
      audioFocusRequestBuilder?.setAudioAttributes(audioAttribute)?.setAcceptsDelayedFocusGain(true)
      audioFocusRequestBuilder?.setOnAudioFocusChangeListener(audioFocusListener)
    }

    val powerManager : PowerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, javaClass.name)
    wakeLock?.setReferenceCounted(false)

    playback = AudioPlayer(this)
    playback?.setCompleteCallback(this)

    notification = AudioNotification(this)
    // 因为在 Oreo 之后 startForegroundService 后必须在5秒内 startForeground，否则会出现 ANR
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      updateNotification()
    }

    val componentName = ComponentName(App.instance, AudioButtonRecevier::class.java)
    val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON)
    mediaButtonIntent.component = componentName
    val audioPendingIntent = PendingIntent.getBroadcast(App.instance, 0, mediaButtonIntent, 0)
    mediaSession = MediaSessionCompat(this, getString(R.string.app_name), componentName, audioPendingIntent)
    mediaSession?.setCallback(object : MediaSessionCompat.Callback() {
      override fun onPlay() {
        play()
      }

      override fun onPause() {
        pause()
      }

      override fun onMediaButtonEvent(mediaButtonEvent : Intent?) : Boolean =
          AudioButtonRecevier.handleIntent(this@AudioService, mediaButtonIntent)
    })
    mediaSession?.setMediaButtonReceiver(audioPendingIntent)
    mediaSession?.isActive = true

    audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager?
  }

  private fun acquireWakeLock(milli : Long) {
    wakeLock?.acquire(milli)
  }

  fun getMediaSession() = mediaSession

  private fun requestAudioFocus() : Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      audioManager?.requestAudioFocus(
          audioFocusRequestBuilder?.build()) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
    } else {
      audioManager?.requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC,
          AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
    }
  }

  fun preparing(audio : Audio?) {
    if (audio != null) {
      playback?.prepare(audio)
    }
  }

  override fun onPrepared() {
    prepareStatus?.onPrepared()
  }
  
  override fun onPrepareFailed(error : String) {
    prepareStatus?.onPrepareFailed(error)
  }
  
  fun play() = synchronized(this) {
    if (requestAudioFocus()) {
      if (! playback?.isPlaying() !!) {
        playback?.start()
        updateNotification()
        acquireWakeLock(10000)
  
//        presenter.addDisposable(
//            Flowable.interval(1, TimeUnit.SECONDS)
//                .onBackpressureDrop()
//                .observeOn(AndroidSchedulers.mainThread(), true)
//                .subscribe({
//                  if (AudioPlayerManager.currentAudio() != null && isPlaying()) {
//                    val time : Int? = currentPosition()?.let { position ->
//                      TimeUnit.MILLISECONDS.toSeconds(position).toInt()
//                    }
//                    time?.let { t ->
//                      AudioPlayerManager.currentAudio()?.uuid?.let { uuid ->
//                        AudioPlayerManager.currentAudio()?.id?.let { id ->
//                          presenter.keepMediaAlive(id, t, uuid)
//                        }
//                      }
//                    }
//                  }
//                }, { t : Throwable? -> Log.e(javaClass.simpleName, t?.toString()) })
//        )
      }
    }
  }

  fun isPlaying() : Boolean {
    if (playback == null) return false
    return playback?.isPlaying() !!
  }

  fun pause() {
    if (playback != null && playback?.isPlaying() !!) {
      playback?.pause()
      updateNotification()
    }
  }
  
  fun stop() {
    if (playback != null) {
      playback?.stop()
      updateNotification()
    }
  }

  fun resume() {
    if (playback != null) {
      playback?.resume()
      updateNotification()
    }
  }
  
  fun seekTo(second : Int) {
    if (playback != null) {
      playback?.seekTo(second)
    }
  }

  fun duration() : Long? = playback?.duration()?.toLong()

  fun currentPosition() : Long? = playback?.currentPosition()?.toLong()

  override fun onCompelte() {
    if (wakeLock?.isHeld !!) {
      wakeLock?.release()
    }
//    presenter.clearDisposable()
  }

  override fun onStartCommand(intent : Intent?, flags : Int, startId : Int) : Int {
    if (intent != null && intent.action != null) {
      val action = intent.action
      when (action) {
        ACTION_PLAY_PAUSE -> {
          if (isPlaying()) {
            pause()
          } else {
            play()
          }
          updateNotification()
          notifyPlayStateChanged()
        }
      }

    }
    return START_STICKY
  }

  private fun updateNotification() {
    if (notification != null) {
      notification?.update()
    }
  }

  private fun notifyPlayStateChanged() {
    val state = if (isPlaying()) {
      PlaybackStateCompat.STATE_PLAYING
    } else {
      PlaybackStateCompat.STATE_PAUSED
    }
    val position : Long = if (currentPosition() == null) {
      0
    } else {
      this.currentPosition() !!
    }
    mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
        .setActions(MEDIA_SESSION_ACTIONS)
        .setState(state, position, 1f)
        .build())
  }
  
  override fun onDestroy() {
    super.onDestroy()
  
    if (playback != null) {
      playback?.release()
    }
    AudioPlayerManager.setupAudio(null)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      audioManager?.abandonAudioFocusRequest(audioFocusRequestBuilder?.build())
    } else {
      audioManager?.abandonAudioFocus(audioFocusListener)
    }
    notification?.stop()
    mediaSession?.isActive = false
    wakeLock?.release()
//    presenter.clearDisposable()
    
    Log.i(javaClass.simpleName, "AudioService is destroyed")
  }
  
//  override fun showMsg(msg : String, vararg objects : Any) {
//    Log.e(javaClass.simpleName, msg)
//  }
//
//  override fun keepMediaAliveSuccess() {
//  }
  
  
  inner class AudioBinder : Binder() {
    val service : AudioService
    get() = this@AudioService
  }

  interface PrepareStatus {
    fun onPrepared()
    fun onPrepareFailed(error : String)
  }
  
  fun withPrepareStatus(status : PrepareStatus) : AudioService{
    this.prepareStatus = status
    return this
  }
  
  fun removePrepareStatus() {
    this.prepareStatus = null
  }
}